package multithreading;

public class Demo10_ThreadCommunication {

    /**
     * 普通的多线程执行，在CPU内部进行随机切换
     * 在某些特定场景下，需要线程与线程之间进行有规律的执行
     * 在多线程之间需要协调任务的一种工作就是线程通信
     *
     * 实现线程通信
     *   wait:
     *      等待，当前线程自己进入了阻塞状态，释放了当前线程对锁的拥有权
     *      在其他线程调用 notify/notifyAll 当前线程被唤醒 可以继续执行代码
     *      被唤醒后 从 wait 的位置继续往后执行代码
     *   notify
     *     唤醒一个等待当前对象的锁的线程(锁池中的线程)，唤醒此对象监视器等待的单个线程
     *   notifyAll
     *     唤醒此对象监视器等待的所有线程
     */
    public static void main(String[] args) {

        DessertShop ds = new DessertShop(5);

        //不管是生产者还是消费者必须共享同一个甜品店的属性和方法
        new Thread(new BreadProducer(ds),"生产者").start();
        new Thread(new BreadConsumer(ds),"消费者").start();

    }

}

/**
 * 定义一个甜品店的类
 *   构造方法：每次制作面包数量的上限
 *   方法：制作面包  和  售卖面包
 *   属性：每次制作面包数量的上限  和  临时的面包数
 */
class DessertShop{

    //每次制作面包数量的上限
    int totalBreadCount;
    //临时的面包数
    int tempBreadCount;

    //创建类的时候给定面包的数量上限
    public DessertShop(int totalBreadCount) {
        this.totalBreadCount = totalBreadCount;
    }


    /**
     * 制作面包的方法
     */
    public synchronized void produceBread(){

        try{
            System.out.println("开始制作面包");
            //循环制作面包，直到制作的面包数达到了上限
            while(this.tempBreadCount<this.totalBreadCount){
                //每制作一个面包给一句提示  ++this.tempBreadCount 就是制作面包
                System.out.println(Thread.currentThread().getName()+"制作了一个面包,现在有"+
                        (++this.tempBreadCount)+"个面包");

                Thread.sleep(500);
            }

            System.out.println("面包制作完毕，可以售卖");

            //面包已经制作完毕，唤醒售卖面包的线程，可以售卖
            notify();
            //当前线程阻塞
            wait();

        }catch (Exception e){
            e.printStackTrace();
        }

    }

    /**
     * 卖面包的方法
     */
    public synchronized void sailBread(){

        try{
            //每次只卖一个面包，所以不用循环操作
            if(this.tempBreadCount<=0){
                System.out.println("面包已售罄，需要制作面包");
                //没有面包了，唤醒制作面包的线程，开始制作面包
                notify();
                //在面包制作完毕之前不售卖
                wait();
            }else{
                System.out.println(Thread.currentThread().getName()+"买了一个面包,剩余"+
                        (--this.tempBreadCount)+"个面包");
            }

            Thread.sleep(100);
        }catch (Exception e){
            e.printStackTrace();
        }


    }
}

/**
 * 定义一个面包生产者的线程类
 * 主方法中通过此类创建一个生产面包的线程
 */
class BreadProducer implements Runnable{

    DessertShop ds = null;

    public BreadProducer(DessertShop ds) {
        this.ds = ds;
    }

    @Override
    public void run() {
        while(true){
            ds.produceBread();
        }
    }
}

/**
 * 定义一个面包消费者者的线程类
 * 主方法中通过此类创建一个售卖面包的线程
 */
class BreadConsumer implements Runnable{

    DessertShop ds = null;

    public BreadConsumer(DessertShop ds) {
        this.ds = ds;
    }

    @Override
    public void run() {
        while (true){
            ds.sailBread();
        }
    }
}
